import { Token } from '@plugins/token'
import axios, { AxiosRequestConfig, Canceler } from 'axios'
import QueryString from 'qs'
import router from '../router'

const CancelAxios = {
  keys: new WeakMap() as WeakMap<AxiosRequestConfig, string>,
  request: new Map() as Map<string, Canceler>,
  symbol: 'cancel request',
  /**
   * 根据请求 config 生成唯一 key
   */
  key(config: AxiosRequestConfig) {
    if (!this.keys.has(config)) {
      if (typeof config.data === 'object') {
        config.data = QueryString.stringify(config.data)
      }
      this.keys.set(config, [config.method, config.url, config.data].join('--'))
    }
    return this.keys.get(config)!
  },
  /**
   * 记录请求
   */
  record(config: AxiosRequestConfig) {
    const key = this.key(config)
    if (this.request.has(key)) {
      this.request.get(key)!(this.symbol)
    }
    config.cancelToken = new axios.CancelToken((canceler) => {
      this.request.set(key, canceler)
    })
    return this
  },
  /**
   * 删除请求记录
   */
  unRecord(config: AxiosRequestConfig) {
    let cancel
    const key = this.keys.get(config)
    if (key) {
      this.keys.delete(config)
      cancel = this.request.get(key)
      this.request.delete(key)
    }
    return cancel
  },
  /**
   * 取消指定的请求
   */
  cancel(config: AxiosRequestConfig) {
    const cancel = this.unRecord(config)
    cancel && cancel(this.symbol)
    return this
  },
  /**
   * 取消所有的请求
   */
  clear() {
    this.request.forEach((cancel, key) => {
      this.request.delete(key)
      cancel(this.symbol)
    })
    return this
  },
}

const $axios = axios.create({
  baseURL: '/api/',
  timeout: 3000,
})

const unValidToken = [
  'getQiNiuToken', // 七牛云 token
  'addIdolInfo', // idol 用户注册
  'loinWeb', // idol 用户登录
]

$axios.interceptors.request.use(
  function (config) {
    const end = config.url!.split('/').pop() || ''
    const token = Token.get()
    if (!unValidToken.includes(end)) {
      if (!token) {
        router.push('/login')
        throw new Error('请前往登录！！！')
      }
      config.headers!.TOKEN = token
      config.headers!.USER_ID = Token.id()
      config.headers!.LOGIN_TYPE = '2'
    }
    config.data = QueryString.stringify(config.data)
    CancelAxios.record(config)
    return config
  },
  function (error) {
    return Promise.reject(error)
  }
)

$axios.interceptors.response.use(
  function (response) {
    if (response.data instanceof Object && 'code' in response.data) {
      const code = parseInt(response.data.code)
      if (code === 1) {
        Token.refresh()
        response.code = code
        response.message = response.data.msg
        response.data = response.data.data
        return response
      }
      return Promise.reject(new Error(response.data.msg))
    }
    return Promise.reject(new Error(response.statusText))
  },
  function (error) {
    return Promise.reject(error)
  }
)

export { $axios, CancelAxios }
